package main

import (
	"context"
	"encoding/json"
	"fmt"
	"reflect"
	"time"

	"github.com/olivere/elastic/v7"
)

var host = "http://119.45.136.232:9200/"

// 索引mapping定义，这里仿微博消息结构定义
const mapping = `
{
  "mappings": {
    "properties": {
      "Title": {
        "type": "text",
		"analyzer": "ik_max_word"
      },
      "Content": {
        "type": "text",
		"analyzer": "ik_max_word"
      },
      "Author": {
        "type": "text",
		"analyzer": "ik_max_word"
      },
      "Created": {
        "type": "date"
      }
    }
  }
}`

type Article struct {
	Id      string    //ID
	Title   string    // 文章标题
	Content string    // 文章内容
	Author  string    // 作者
	Created time.Time // 发布时间
}

//获取客户端
func GetEsClient() *elastic.Client {
	var client *elastic.Client
	var err error
	//这个地方有个小坑 不加上elastic.SetSniff(false) 会连接不上
	client, err = elastic.NewClient(elastic.SetSniff(false), elastic.SetURL(host))
	if err != nil {
		panic(err)
	}
	_, _, err = client.Ping(host).Do(context.Background())
	if err != nil {
		panic(err)
	}
	_, err = client.ElasticsearchVersion(host)
	if err != nil {
		panic(err)
	}
	return client
}

//创建Index
func CheckIndex(index string) {
	client := GetEsClient()
	// 执行ES请求需要提供一个上下文对象
	ctx := context.Background()
	// 首先检测下weibo索引是否存在
	exists, err := client.IndexExists(index).Do(ctx)
	if err != nil {
		panic(err)
	}
	if !exists {
		// weibo索引不存在，则创建一个
		_, err := client.CreateIndex(index).BodyString(mapping).Do(ctx)
		if err != nil {
			// Handle error
			panic(err)
		}
	}
	fmt.Println(exists)
}

//创建数据   index：索引   id：主键    data：索引mapping定义
func CreateNews(index string, id string, data interface{}) (string, error) {
	// 创建创建一条微博
	// msg1 := Weibo{User: "olivere", Message: "打酱油的一天", Retweets: 0}
	client := GetEsClient()
	// 使用client创建一个新的文档
	put1, err := client.Index().
		Index(index).            // 设置索引名称
		Id(id).                  // 设置文档id
		BodyJson(data).          // 指定前面声明的微博内容
		Do(context.Background()) // 执行请求，需要传入一个上下文对象
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("文档Id %s, 索引名 %s\n", put1.Id, put1.Index), err
}

//通过主键ID搜索  调用案例：QueryId("esik", "2", Article{})
func QueryId(index, id string, doc interface{}) (interface{}, error) {
	// 根据id查询文档
	client := GetEsClient()
	get1, err := client.Get().
		Index(index).            // 指定索引名
		Id(id).                  // 设置文档id
		Do(context.Background()) // 执行请求
	if err != nil {
		return "", err
	}
	msg2 := doc
	if get1.Found {
		// fmt.Println(get1)
		fmt.Printf("文档id=%s , 索引名=%s\n", get1.Id, get1.Index)
		// 手动将文档内容转换成go struct对象
		// 提取文档内容，原始类型是json数据
		data, _ := get1.Source.MarshalJSON()
		// 将json转成struct结果
		json.Unmarshal(data, &msg2)
	}
	return msg2, err
}

//根据ID 更新文档
func UpdateDoc(index, id string, mapData map[string]interface{}) error {
	client := GetEsClient()
	_, err := client.Update().
		Index(index).            // 设置索引名
		Id(id).                  // 文档id
		Doc(mapData).            // map[string]interface{}{"content": "今天不打酱油222"}  key小写
		Do(context.Background()) // 执行ES查询
	return err
}

//删除文档
func DelDoc(index, id string) error {
	client := GetEsClient()
	// 根据id删除一条数据
	_, err := client.Delete().
		Index(index).
		Id(id).
		Do(context.Background())
	return err
}

//删除索引
func DelIndex(index string) error {
	client := GetEsClient()
	_, err := client.DeleteIndex(index).Do(context.Background())
	return err
}

func Query(index string) {
	// 执行ES请求需要提供一个上下文对象
	ctx := context.Background()
	// 创建term查询条件，用于精确查询
	// termQuery := elastic.NewMatchQuery("Title", "是王")
	client := GetEsClient()
	// 创建bool查询
	boolQuery := elastic.NewBoolQuery().Must()
	// 创建term查询
	// termQuery := elastic.NewTermQuery("Title", "你是海贼王")
	matchQuery := elastic.NewMatchQuery("Title", "你别不是中毒了吧")
	// 设置bool查询的must条件, 组合了两个子查询
	// 表示搜索匹配Author=tizi且Title匹配"golang es教程"的文档
	boolQuery.Must(matchQuery)
	searchResult, err := client.Search().
		Index(index).     // 设置索引名
		Query(boolQuery). // 设置查询条件
		// Sort("Created", true). // 设置排序字段，根据Created字段升序排序，第二个参数false表示逆序
		From(0).      // 设置分页参数 - 起始偏移量，从第0行记录开始
		Size(3).      // 设置分页参数 - 每页大小
		Pretty(true). // 查询结果返回可读性较好的JSON格式
		Do(ctx)       // 执行请求
	if err != nil {
		// Handle error
		panic(err)
	}
	fmt.Printf("查询消耗时间 %d ms, 结果总数: %d\n", searchResult.TookInMillis, searchResult.TotalHits())
	if searchResult.TotalHits() > 0 {
		// 查询结果不为空，则遍历结果
		var b1 Article
		// 通过Each方法，将es结果的json结构转换成struct对象
		for _, item := range searchResult.Each(reflect.TypeOf(b1)) {
			// 转换成Article对象
			if t, ok := item.(Article); ok {
				fmt.Println(t)
			}
		}
	}
}

func main() {
	//新建文档
	msg1 := Article{Id: "4", Content: "海贼王这是", Title: "你可能是中毒了", Author: "小明", Created: time.Now()}
	CreateNews("esiksss", "1", msg1)

	//根据ID查询
	// fmt.Println(QueryId("esik", "2", Article{}))
	// UpdateDoc("weibo", "1", map[string]interface{}{"content": "今天不打酱油222"})
	// fmt.Println(tool.QueryId("weibo", "1"))
	// fmt.Print(DelDoc("weibo", "1")
	// DelIndex("esik")
	// Query("esik")
	// fmt.Println()

}
